package com.dmall.opening.up.records.util;

import com.dmall.opening.up.core.JsonType;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.joda.PackageVersion;
import org.joda.time.DateTime;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public final class Jsons {

    private static final TypeReference<Object> OBJECT_TYPE_REFERENCE = new TypeReference<Object>() {
    };

    private static final TypeReference<HashMap<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<HashMap<String, Object>>() {
    };

    private static final TypeReference<LinkedList<Object>> LINKED_LIST_OF_OBJECT_TYPE_REFERENCE = new TypeReference<LinkedList<Object>>() {
    };

    private static final TypeReference<LinkedList<Map<String, Object>>> LINKED_LIST_OF_MAP_TYPE_REFERENCE = new TypeReference<LinkedList<Map<String, Object>>>() {
    };

    private static final ObjectMapper MAPPER = new ObjectMapper();

    public static String toJsonString(Object value) {
        try {
            return MAPPER.writeValueAsString(value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Map<String, Object> toJsonObject(String json) {
        try {
            return MAPPER.readValue(json, MAP_TYPE_REFERENCE);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static List<Map<String, Object>> toJsonArray(String json) {
        try {
            return MAPPER.readValue(json, LINKED_LIST_OF_MAP_TYPE_REFERENCE);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Object toObject(String json) {
        try {
            return MAPPER.readValue(json, OBJECT_TYPE_REFERENCE);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static List<Object> toObjectArray(String json) {
        try {
            return MAPPER.readValue(json, LINKED_LIST_OF_OBJECT_TYPE_REFERENCE);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    static {
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        MAPPER.registerModule(new Jsons.JodaTimeModule());
        MAPPER.registerModule(new Jsons.JsonTypeModule());
    }

    static class JsonTypeModule extends SimpleModule {
        public JsonTypeModule() {
            super(PackageVersion.VERSION);
            this.addSerializer(JsonType.class, new JsonSerializer<JsonType>() {
                public void serialize(JsonType value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
                    jsonGenerator.writeObject(value.toJson());
                }
            });
        }
    }

    static class JodaTimeModule extends SimpleModule {
        public JodaTimeModule() {
            super(PackageVersion.VERSION);
            this.addSerializer(DateTime.class, new JsonSerializer<DateTime>() {
                public void serialize(DateTime value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
                    jsonGenerator.writeNumber(value.getMillis());
                }
            });
        }
    }
}
